{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "二值化\n",
    "降噪\n",
    "倾斜矫正\n",
    "~~Thinning & Skeletonization 让字变瘦~~\n",
    "缩放 > 300ppi"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "1. BM3D 降噪\n",
    "2. Otsu 滤波\n",
    "3. 倾斜矫正"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "# np.array(img)\n",
    "# Image.fromarray(ndarray)"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from PIL import Image  # use pillow to convert to grayscale\n",
    "import cv2\n",
    "\n",
    "imgfilename = \"fa.png\"\n",
    "img = Image.open(imgfilename)\n",
    "img_gray = np.array(img.convert(\"L\"))  # convert to grayscale\n",
    "# do not confused by cv2.IMREAD_GRAYSCALE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [],
   "source": [
    "## Sharpen\n",
    "from PIL import ImageFilter, ImageEnhance\n",
    "\n",
    "def filter_sharpen(image: Image.Image):\n",
    "    image = Image.fromarray(image).filter(ImageFilter.UnsharpMask)\n",
    "# ImageEnhancer.enhance can be implemented using UnsharpMask, while ImageFilter.SHARPEN is a magic kernel.\n",
    "\n",
    "img_gray = Image.fromarray(img_gray).filter(ImageFilter.SHARPEN)\n",
    "\n",
    "# old method\n",
    "def filter_imageenhance(image: Image.Image):\n",
    "    enhancer = ImageEnhance.Sharpness(Image.fromarray(image))\n",
    "    image = enhancer.enhance(1.5)\n",
    "    image = np.array(image)"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "outputs": [],
   "source": [
    "# https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html\n",
    "def binary_otsu(image):\n",
    "    ret, otsu = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    return ret, otsu"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "outputs": [
    {
     "ename": "error",
     "evalue": "OpenCV(4.6.0) /io/opencv/modules/photo/src/denoising.cpp:178: error: (-5:Bad argument) Type of input image should be CV_8UC3 or CV_8UC4! in function 'fastNlMeansDenoisingColored'\n",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31merror\u001B[0m                                     Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[21], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m Image\u001B[38;5;241m.\u001B[39mfromarray(\u001B[43mrun_NlMeans\u001B[49m\u001B[43m(\u001B[49m\u001B[43mnp\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43marray\u001B[49m\u001B[43m(\u001B[49m\u001B[43mimg_gray\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m)\u001B[38;5;241m.\u001B[39mshow()\n",
      "Cell \u001B[0;32mIn[20], line 4\u001B[0m, in \u001B[0;36mrun_NlMeans\u001B[0;34m(image)\u001B[0m\n\u001B[0;32m----> 4\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mrun_NlMeans\u001B[39m(image): \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mcv2\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mfastNlMeansDenoisingColored\u001B[49m\u001B[43m(\u001B[49m\u001B[43mimage\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m10\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m10\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m7\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m15\u001B[39;49m\u001B[43m)\u001B[49m\n",
      "\u001B[0;31merror\u001B[0m: OpenCV(4.6.0) /io/opencv/modules/photo/src/denoising.cpp:178: error: (-5:Bad argument) Type of input image should be CV_8UC3 or CV_8UC4! in function 'fastNlMeansDenoisingColored'\n"
     ]
    }
   ],
   "source": [
    "Image.fromarray(run_NlMeans(np.array(img_gray))).show()"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "outputs": [],
   "source": [
    "# https://mp.weixin.qq.com/s?__biz=MzI5MjYzNzAyMw==&mid=2247484153&idx=1&sn=b65e9e99047ae20ed44cd99e4b0ff2e0#wechat_redirect\n",
    "\n",
    "# fastNlMeans\n",
    "def run_NlMeans(image): return cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 15)\n",
    "# BM3D算法\n",
    "def run_bm3d(noisy_im, sigma,\n",
    "             n_H, k_H, N_H, p_H, tauMatch_H, useSD_H, tau_2D_H, lambda3D_H,\n",
    "             n_W, k_W, N_W, p_W, tauMatch_W, useSD_W, tau_2D_W):\n",
    "    k_H = 8 if (tau_2D_H == 'BIOR' or sigma < 40.) else 12\n",
    "    k_W = 8 if (tau_2D_W == 'BIOR' or sigma < 40.) else 12\n",
    "\n",
    "    noisy_im_p = symetrize(noisy_im, n_H)\n",
    "    img_basic = bm3d_1st_step(sigma, noisy_im_p, n_H, k_H, N_H, p_H, lambda3D_H, tauMatch_H, useSD_H, tau_2D_H)\n",
    "    img_basic = img_basic[n_H: -n_H, n_H: -n_H]\n",
    "\n",
    "    assert not np.any(np.isnan(img_basic))\n",
    "    img_basic_p = symetrize(img_basic, n_W)\n",
    "    noisy_im_p = symetrize(noisy_im, n_W)\n",
    "    img_denoised = bm3d_2nd_step(sigma, noisy_im_p, img_basic_p, n_W, k_W, N_W, p_W, tauMatch_W, useSD_W, tau_2D_W)\n",
    "    img_denoised = img_denoised[n_W: -n_W, n_W: -n_W]\n",
    "\n",
    "    return img_basic, img_denoised"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "## 倾斜校正\n",
    "# 度数转换\n",
    "def DegreeTrans(theta):\n",
    "    res = theta / np.pi * 180\n",
    "    return res\n",
    "\n",
    "# 逆时针旋转图像degree角度（原尺寸）\n",
    "def rotateImage(src, degree):\n",
    "    # 旋转中心为图像中心\n",
    "    h, w = src.shape[:2]\n",
    "    # 计算二维旋转的仿射变换矩阵\n",
    "    RotateMatrix = cv2.getRotationMatrix2D((w / 2.0, h / 2.0), degree, 1)\n",
    "    print(RotateMatrix)\n",
    "    # 仿射变换，背景色填充为白色\n",
    "    rotate = cv2.warpAffine(src, RotateMatrix, (w, h), borderValue=(255, 255, 255))\n",
    "    return rotate\n",
    "\n",
    "# 通过霍夫变换计算角度\n",
    "def CalcDegree(srcImage):\n",
    "    midImage = cv2.cvtColor(srcImage, cv2.COLOR_BGR2GRAY)\n",
    "    dstImage = cv2.Canny(midImage, 50, 200, 3)\n",
    "    lineimage = srcImage.copy()\n",
    "    # 通过霍夫变换检测直线\n",
    "    # 第4个参数就是阈值，阈值越大，检测精度越高\n",
    "    lines = cv2.HoughLines(dstImage, 1, np.pi / 180, 200)\n",
    "    # 由于图像不同，阈值不好设定，因为阈值设定过高导致无法检测直线，阈值过低直线太多，速度很慢\n",
    "    sum = 0\n",
    "    # 依次画出每条线段\n",
    "    for i in range(len(lines)):\n",
    "        for rho, theta in lines[i]:\n",
    "            # print(\"theta:\", theta, \" rho:\", rho)\n",
    "            a = np.cos(theta)\n",
    "            b = np.sin(theta)\n",
    "            x0 = a * rho\n",
    "            y0 = b * rho\n",
    "            x1 = int(round(x0 + 1000 * (-b)))\n",
    "            y1 = int(round(y0 + 1000 * a))\n",
    "            x2 = int(round(x0 - 1000 * (-b)))\n",
    "            y2 = int(round(y0 - 1000 * a))\n",
    "            # 只选角度最小的作为旋转角度\n",
    "            sum += theta\n",
    "            cv2.line(lineimage, (x1, y1), (x2, y2), (0, 0, 255), 1, cv2.LINE_AA)\n",
    "            cv2.imshow(\"Imagelines\", lineimage)\n",
    "\n",
    "    # 对所有角度求平均，这样做旋转效果会更好\n",
    "    average = sum / len(lines)\n",
    "    angle = DegreeTrans(average) - 90\n",
    "    return angle"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "outputs": [],
   "source": [
    "## Thinning\n",
    "# # consider using sharpen method instead\n",
    "# kernel = np.ones((1,1),np.uint8)\n",
    "# erosion = cv2.erode(img_gray, kernel, iterations = 1)\n"
   ],
   "metadata": {
    "collapsed": false
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
